class_name QueryManager
extends Node

var component_groups := {}
var mutils := {}
var singles := {}
var system_manager: Node
var entities: Entities


func _init(_system_manager: Node) -> void:
	system_manager = _system_manager
	entities = Entities.new()


func _ready() -> void:
	# warning-ignore: return_value_discarded
	connect("tree_exiting", self, "_on_tree_exiting")
	var tree: SceneTree = get_tree()
	# warning-ignore: return_value_discarded
	tree.connect("node_added", self, "_on_node_added")
	# warning-ignore: return_value_discarded
	tree.connect("node_removed", self, "_on_node_removed")


func ready() -> void:
	var entity_group =  get_tree().get_nodes_in_group("Entity")
	for entity in entity_group:
		update_multi_component(entity)
	for entity in entity_group:
		entity.setup(self)


func register_requirements(system: System) -> void:
	var requirements = system.requirements
	for item in requirements:
		var ref = funcref(system, item.func)
		var read = item.read
		if item.has("create"):
			add_create_listener(ref, read, item.create)
		elif item.has("modify"):
			add_modify_listener(ref, item.read, item.modify)
		elif item.has("destroy"):
			add_destroy_listener(ref, item.read, item.destroy)


func add_create_listener(ref, read, create) -> void:
	for component_name in create:
		var multi = fetch_data(component_name)
		if not multi:
			continue
		multi.add_create_listener(ref, fetch_components(read))


func add_modify_listener(ref, read, modify) -> void:
	for component_name in modify:
		var multi = fetch_data(component_name)
		if not multi:
			continue
		multi.add_modify_listener(ref, fetch_components(read))


func add_destroy_listener(ref, read, destroy) -> void:
	for component_name in destroy:
		var multi = fetch_data(component_name)
		if not multi:
			continue
		multi.add_destroy_listener(ref, fetch_components(read))


func register_multi(multi: Multi) -> void:
	mutils[multi.name] = multi


func register_single(single: Single) -> void:
	single.data.set("query", self)
	singles[single.name] = single


func fetch_data(component_name) -> Node:
	if mutils.has(component_name):
		return mutils.get(component_name)
	elif singles.has(component_name):
		return singles.get(component_name)
	elif component_name == "Entities":
		return entities
	return null


func fetch_single(single_name) -> Node:
	return singles.get(single_name)


func fetch_component(component_name):
	return mutils.get(component_name)


# catch system function call args
func fetch_components(read: Array):
	var args = []
	if not read.size():
		return args
	for component_name in read:
		var data = fetch_data(component_name)
		args.append(data)
	return args


# add componet to multi
func update_multi_component(entity: Entity) -> void:
	var components = entity.components
	for component in components:
		var multi = fetch_data(component.component_name)
		if not multi:
			print("component multi not register:", component.component_name)
			continue
		multi.insert(entity.id, component)


func _on_node_added(node: Node) -> void:
	if ECSTool.is_component(node):
		var multi = fetch_data(node.name)
		if multi:
			multi.insert(node)
		else:
			print("QueryManager: component not registered:", node.name)
	elif ECSTool.is_entity(node):
		entities.create(node.get("id"))
		#update_component_groups(node)


func _on_node_removed(node: Node) -> void:
	if ECSTool.is_component(node):
		var entity: Entity = node.get_parent()
		if not entity:
			return
		entity.unregister_component(node)
		#update_component_groups(entity)
	elif ECSTool.is_entity(node):
		for component_group in component_groups:
			var entity_list: Array = component_groups[component_group]
			entity_list.erase(node.id)


func _on_tree_exiting() -> void:
	var tree: SceneTree = get_tree()
	tree.disconnect("node_added", self, "_on_node_added")
	tree.disconnect("node_removed", self, "_on_node_removed")
	disconnect("tree_exiting", self, "_on_tree_exiting")
